package ch.simplix.android.net.discoverer.bluetooth;

import java.util.concurrent.TimeUnit;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import ch.simplix.android.repetitive.service.implementation.ARepetitiveService;

/**
 * {@link BluetoothDiscovererService} is responsible for scanning the bluetooth
 * network and gathering measurement so that the server can try to locate the
 * client.
 * 
 * @author SimpliX
 * 
 */
public class BluetoothDiscovererService extends ARepetitiveService {

	/** Action being broadcasted whenever a new measurement was retrieved */
	public static String BLUETOOTH_ACTION = "ch.simplix.android.net.discoverer.bluetooth.BLUETOOTH_DISCOVERER_SERVICE";

	private static final String TAG = BluetoothDiscovererService.class
			.getSimpleName();

	private BluetoothAdapter bluetoothAdapter;
	private boolean wasBluetoothAdapterDisabled;
	private BluetoothMeasurement latestMeasurement;
	private BluetoothMeasurement activeMeasurement;

	private IntentFilter deviceFoundFilter = new IntentFilter(
			BluetoothDevice.ACTION_FOUND);
	private IntentFilter discoveryFinishedFilter = new IntentFilter(
			BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
	private IntentFilter discoveryStartedFilter = new IntentFilter(
			BluetoothAdapter.ACTION_DISCOVERY_STARTED);

	private IntentFilter stateChangedFilter = new IntentFilter(
			BluetoothAdapter.ACTION_STATE_CHANGED);

	@Override
	public void onStart(Intent intent) {
		start();
	}

	public void discover() {
		if (!isRunning()) {
			start();
			scan();
		}
	}

	private void start() {
		if (bluetoothAdapter != null)
			return;
		bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		// Register the BroadcastReceiver
		registerReceiver(resultReceiver, deviceFoundFilter);
		registerReceiver(resultReceiver, discoveryFinishedFilter);
		registerReceiver(resultReceiver, discoveryStartedFilter);
		registerReceiver(stateReceiver, stateChangedFilter);
	}

	/**
	 * Retrieve measurement from last scan.
	 * 
	 * @return {@link BluetoothMeasurement}
	 */
	public BluetoothMeasurement getLatestMeasurement() {
		return latestMeasurement;
	}

	/**
	 * Stop scanning and broadcasting new measurements.
	 */
	@Override
	public void onStop() {
		stop();
	}

	private void stop() {
		Log.d(TAG, "stopping");
		if (bluetoothAdapter == null)
			return;
		unregisterReceiver(resultReceiver);
		unregisterReceiver(stateReceiver);
		bluetoothAdapter.cancelDiscovery();
		if (wasBluetoothAdapterDisabled) {
			bluetoothAdapter.disable();
		}

		bluetoothAdapter = null;
		Log.d(TAG, "stop");

	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	/**
	 * @see Binder
	 */
	public class LocalBinder extends Binder {
		public BluetoothDiscovererService getService() {
			return BluetoothDiscovererService.this;
		}
	}

	private final LocalBinder binder = new LocalBinder();

	/**
	 * Try to enable the Bluetooth adapter if it is not already enabled or not
	 * currently being enabled. Otherwise start the scanning directly.
	 */
	private void scan() {
		Log.d(TAG, "scanning");
		if (bluetoothAdapter == null)
			start();
		if (!bluetoothAdapter.isEnabled()) {
			if (bluetoothAdapter.getState() != BluetoothAdapter.STATE_TURNING_ON) {
				wasBluetoothAdapterDisabled = true;
				if (!bluetoothAdapter.enable())
					return;
			}
		} else {
			Log.d(TAG, "discovering");
			bluetoothAdapter.startDiscovery();
		}
	}

	/**
	 * {@link BroadcastReceiver} for reacting on the current bluetooth state. If
	 * the bluetooth adapter is enabled, start a scan.
	 */
	private final BroadcastReceiver stateReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {

			String action = intent.getAction();
			Log.d(TAG, action);
			if (bluetoothAdapter != null && bluetoothAdapter.isEnabled())
				scan();
		}

	};

	/**
	 * {@link BroadcastReceiver} for retrieving scanning results and to creating
	 * a new {@link BluetoothMeasurement}. The measurement is then stored in
	 * {@link BluetoothMeasurement}.
	 */
	private final BroadcastReceiver resultReceiver = new BroadcastReceiver() {
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			Log.d(TAG, "The code: " + action);
			// When discovery finds a device
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				// Get the BluetoothDevice object from the Intent
				BluetoothDevice device = intent
						.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,
						Short.MIN_VALUE);
				BluetoothReading reading = new BluetoothReading();
				reading.setBluetoothDevice(device);
				reading.setBluetoothAddress(device.getAddress());
				reading.setFriendlyName(device.getName());
				reading.setMajorDeviceClass(""
						+ device.getBluetoothClass().getMajorDeviceClass());
				reading.setMinorDeviceClass(""
						+ (device.getBluetoothClass().getDeviceClass() ^ device
								.getBluetoothClass().getMajorDeviceClass()));
				reading.setRssi(rssi);
				activeMeasurement.addBluetoothReading(reading);
				Log.d(TAG, "Found");

			} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
					.equals(action)) {
				BluetoothDiscovererService.this.latestMeasurement = activeMeasurement;

				activeMeasurement = null;

				Intent bluetoothIntent = new Intent(BLUETOOTH_ACTION);
				bluetoothIntent.putExtra("Measurement",
						BluetoothDiscovererService.this.latestMeasurement
								.toString());
				Log.i(TAG, "Finished... Sending Broadcast");
				sendBroadcast(bluetoothIntent);
				if (!isRunning()) {
					stop();
				}
			} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {

				activeMeasurement = new BluetoothMeasurement();

				Log.d(TAG, "Started...");
			} else
				Log.d(TAG, "Other code: " + action);

		}
	};

	@Override
	public void onRepeat() {
		scan();

	}
}